<script setup lang="ts">
import { ref, onMounted, nextTick, onBeforeUnmount, computed, watchEffect } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { message, Input, Button, Avatar, Spin, Card, Modal, Tooltip, Alert, Tag } from 'ant-design-vue';
import {
  SendOutlined,
  RocketOutlined,
  LeftOutlined,
  ReloadOutlined,
  LoadingOutlined,
  CheckCircleOutlined,
  FullscreenOutlined,
  FullscreenExitOutlined,
  CodeOutlined,
  EyeOutlined,
  EyeInvisibleOutlined,
  DownloadOutlined,
} from '@ant-design/icons-vue';
import { useUserStore } from '@/stores/user';
import { useSSEStore } from '@/stores/sse';
import { getAppVoById, chatGenCode, deployApp, downloadAppCode } from '@/api/appController';
import { listAppChatHistory } from '@/api/chatHistoryController';
import MarkdownViewer from '@/components/MarkdownViewer.vue';
import PreviewPane from '@/components/PreviewPane.vue';
import type { SelectedElementInfo } from '@/utils/iframeVisualEditor';

// 路由参数
const route = useRoute();
const router = useRouter();
const userStore = useUserStore();
const sseStore = useSSEStore();
const appIdStr = ref(route.params.id as string);
const appId = computed(() => String(appIdStr.value));

// 应用信息
const appInfo = ref<API.AppVO | null>(null);
const loading = ref(true);
const chatLoading = ref(false);
const codeGenerated = ref(false);
const deployLoading = ref(false);
const deployed = ref(false);
const downloadLoading = ref(false);

// 消息相关
type ChatMessage = {
  id: string;
  message?: string;
  messageType?: string;
  appId?: string;
  userId?: string | number | null;
  createTime?: string;
  updateTime?: string;
  isDelete?: number;
};
const chatHistory = ref<ChatMessage[]>([]);
const userMessage = ref('');
const previewUrl = ref('');
const eventSource = ref<EventSource | null>(null);
const currentAiMessage = ref('');
const isProcessingMessage = ref(false);
const messagesContainerRef = ref<HTMLElement | null>(null);

// 编辑模式与选择
const isEditMode = ref(false);
const hoveredElement = ref<SelectedElementInfo | null>(null);
const selectedElements = ref<SelectedElementInfo[]>([]);
const previewPaneRef = ref<InstanceType<typeof PreviewPane> | null>(null);

// 计算属性 - 按时间排序的聊天历史
const sortedChatHistory = computed(() => {
  return [...chatHistory.value].sort((a, b) => {
    const timeA = new Date(a.createTime || '').getTime();
    const timeB = new Date(b.createTime || '').getTime();
    return timeA - timeB;
  });
});

// 获取应用详情
const fetchAppInfo = async () => {
  try {
    const res = await getAppVoById({ id: appId.value });
    if (res.data?.code === 0 && res.data?.data) {
      appInfo.value = res.data.data;
      const codeType = String(appInfo.value?.codeGenType || 'html').toLowerCase();
      if (codeType.includes('vue')) {
        // Vue 工程模式：使用 dist 下的入口
        previewUrl.value = `/api/static/vue_project_${appId.value}/dist/index.html`;
        // 基于 appId 确保构建进度 SSE 连接存在（不随页面卸载关闭）
        sseStore.ensureBuildSSE(appId.value);
      } else {
        // 其他：保持原有规则
        previewUrl.value = `/api/static/${appInfo.value?.codeGenType || 'html'}_${appId.value}/index.html`;
      }
      if (appInfo.value?.deployKey) {
        deployed.value = true;
      }
      const isNew = route.query.newApp === 'true';
      if (!isNew) {
        codeGenerated.value = true;
      }
      document.title = `${appInfo.value?.appName || '对话'} - AI Generator`;
    } else {
      message.error(res.data?.message || '应用不存在');
      router.push('/');
      return false;
    }
    return true;
  } catch (error) {
    message.error('获取应用详情失败');
    router.push('/');
    return false;
  }
};

// 获取聊天历史
const fetchChatHistory = async () => {
  try {
    const res = await listAppChatHistory({ appId: appId.value as unknown as number, pageSize: 100 });
    if (res.data?.code === 0 && res.data?.data) {
      const records = (res.data.data.records || []) as API.ChatHistory[];
      const mapped: ChatMessage[] = records.map((r) => ({
        id: String((r as any).id ?? ''),
        message: r.message,
        messageType: r.messageType,
        appId: String((r as any).appId ?? appId.value),
        userId: (r as any).userId ?? null,
        createTime: r.createTime,
        updateTime: r.updateTime,
        isDelete: (r as any).isDelete,
      }));
      chatHistory.value = mapped.sort((a, b) => new Date(a.createTime || '').getTime() - new Date(b.createTime || '').getTime());
    }
  } catch (error) {
    console.error('获取聊天历史失败', error);
  } finally {
    loading.value = false;
  }
};

// 发送消息
const sendMessage = async () => {
  if (!userMessage.value.trim()) {
    message.warning('请输入消息');
    return;
  }
  let composedMessage = userMessage.value;
  if (selectedElements.value.length) {
    const blocks = selectedElements.value.map(el => {
      const meta: string[] = [];
      meta.push(`标签: <${el.tagName}>`);
      if (el.id) meta.push(`id: ${el.id}`);
      if (el.classList && el.classList.length) meta.push(`class: ${el.classList.join(' ')}`);
      if (el.text) meta.push(`文本: ${el.text}`);
      if (el.href) meta.push(`href: ${el.href}`);
      if (el.src) meta.push(`src: ${el.src}`);
      if (el.cssSelector) meta.push(`选择器: ${el.cssSelector}`);
      if (el.xpath) meta.push(`XPath: ${el.xpath}`);
      if (el.outerHTML) { meta.push('元素代码:'); meta.push('```html'); meta.push(el.outerHTML); meta.push('```'); }
      return meta.join('\n');
    });
    composedMessage = `${userMessage.value}\n\n[选中元素信息]\n${blocks.join('\n---\n')}`;
  }

  const userMsg: ChatMessage = {
    id: String(Date.now()),
    message: composedMessage,
    messageType: 'user',
    appId: appId.value,
    userId: userStore.loginUser.id ?? null,
    createTime: new Date().toISOString()
  };
  chatHistory.value.push(userMsg);

  const msgToSend = composedMessage;
  userMessage.value = '';

  if (isEditMode.value) {
    selectedElements.value = [];
    previewPaneRef.value?.clearSelection?.();
    isEditMode.value = false;
  }

  await nextTick();
  scrollToBottom();
  startSSEConnection(msgToSend);
};

// SSE连接处理
const startSSEConnection = (msg: string) => {
  chatLoading.value = true;
  isProcessingMessage.value = true;
  currentAiMessage.value = '';

  if (eventSource.value) {
    eventSource.value.close();
  }

  try {
    const apiUrl = 'http://localhost:8080/api/app/gen';
    const body = { appId: appId.value, userMessage: msg };

    fetch(apiUrl, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(body),
      credentials: 'include'
    }).then(response => {
      if (!response.ok) {
        throw new Error(`HTTP error! Status: ${response.status}`);
      }
      const reader = response.body?.getReader();
      if (!reader) {
        throw new Error('无法获取响应流');
      }
      const decoder = new TextDecoder();

      function processStream() {
        if (!reader) {
          finishAiMessage();
          return;
        }

        reader.read().then(({ done, value }) => {
          if (done) {
            finishAiMessage();
            return;
          }
          const text = decoder.decode(value, { stream: true });
          const lines = text.split('\n');
          for (const line of lines) {
            if (!line.trim()) continue;
            try {
              if (line.startsWith('event:done')) {
                finishAiMessage();
                return;
              }
              if (line.startsWith('data:')) {
                const jsonData = line.substring(5).trim();
                try {
                  const data = JSON.parse(jsonData);
                  if (data && data.d !== undefined) {
                    if (typeof data.d === 'string' && (data.d.startsWith('{') || data.d.startsWith('['))) {
                      try {
                        const innerData = JSON.parse(data.d);
                        if (innerData && innerData.html) {
                          currentAiMessage.value = innerData.html.trimStart();
                        } else {
                          if (!currentAiMessage.value) {
                            currentAiMessage.value = data.d.trimStart();
                          } else {
                            currentAiMessage.value += data.d;
                          }
                        }
                      } catch {
                        if (!currentAiMessage.value) {
                          currentAiMessage.value = data.d.trimStart();
                        } else {
                          currentAiMessage.value += data.d;
                        }
                      }
                    } else {
                      if (!currentAiMessage.value) {
                        currentAiMessage.value = data.d.trimStart();
                      } else {
                        currentAiMessage.value += data.d;
                      }
                    }
                  } else if (data && data.html) {
                    currentAiMessage.value = data.html.trimStart();
                  } else {
                    currentAiMessage.value = JSON.stringify(data, null, 2);
                  }
                } catch (parseError) {
                  currentAiMessage.value += jsonData;
                }
                scrollToBottom();
              }
            } catch (e) {
              console.error('处理数据行错误:', e, line);
            }
          }
          processStream();
        }).catch(() => {
          message.error('接收AI回复时发生错误');
          finishAiMessage();
        });
      }
      processStream();
    }).catch(() => {
      message.error('发送请求失败');
      chatLoading.value = false;
      isProcessingMessage.value = false;
    });

  } catch (error) {
    message.error('无法建立连接');
    chatLoading.value = false;
    isProcessingMessage.value = false;
  }
};

// 完成AI消息处理
const finishAiMessage = () => {
  if (eventSource.value) {
    eventSource.value.close();
    eventSource.value = null;
  }

  if (currentAiMessage.value) {
    const cleanedMessage = currentAiMessage.value.trimStart();
    const aiMsg: ChatMessage = {
      id: String(Date.now()),
      message: cleanedMessage,
      messageType: 'assistant',
      appId: appId.value,
      createTime: new Date().toISOString()
    };
    chatHistory.value.push(aiMsg);
    currentAiMessage.value = '';
    codeGenerated.value = true;

    // 若为 Vue 项目，提示正在构建并确保构建进度连接
    const codeType = String(appInfo.value?.codeGenType || '').toLowerCase();
    if (codeType.includes('vue')) {
      try { sseStore.ensureBuildSSE(appId.value); } catch (e) {}
      Modal.info({
        title: '正在构建项目',
        content: 'AI 已生成 Vue 项目内容，正在进行构建，请不要离开此页面。构建完成后请点击预览区上方的刷新按钮以查看效果。',
        okText: '我知道了',
      });
    }
  }

  chatLoading.value = false;
  isProcessingMessage.value = false;
  refreshPreview();
};

// 刷新预览
const refreshPreview = () => { previewPaneRef.value?.reload?.(); };

// 下载应用代码
const handleDownload = async () => {
  if (!appId.value) {
    message.error('无效的应用ID');
    return;
  }
  try {
    downloadLoading.value = true;
    const res = await downloadAppCode(
      { appId: appId.value as unknown as number },
      { responseType: 'blob' }
    );
    const disposition = res.headers?.['content-disposition'] || res.headers?.['Content-Disposition'];
    let filename = `${appInfo.value?.appName || 'app'}-${appId.value}.zip`;
    if (disposition) {
      const match = /filename\*=UTF-8''([^;]+)|filename=\"?([^\";]+)\"?/i.exec(disposition);
      const name = decodeURIComponent(match?.[1] || match?.[2] || '');
      if (name) filename = name;
    }
    const contentType = res.headers?.['content-type'] || 'application/zip';
    const blob = new Blob([res.data], { type: contentType });
    const url = window.URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    document.body.appendChild(link);
    link.click();
    link.remove();
    window.URL.revokeObjectURL(url);
  } catch (error) {
    console.error('下载失败:', error);
    message.error('下载失败，请稍后再试');
  } finally {
    downloadLoading.value = false;
  }
};

// 部署应用
const handleDeploy = async () => {
  if (!codeGenerated.value) {
    message.warning('请先生成网站');
    return;
  }
  deployLoading.value = true;
  try {
    const res = await deployApp({ appId: appId.value as unknown as number });
    if (res.data?.code === 0 && res.data?.data) {
      message.success('部署成功');
      deployed.value = true;
      const deployKey = res.data.data;
      const deployUrl = `http://localhost:8080/api/${deployKey}/`;
      refreshPreview();
      Modal.success({
        title: '部署成功',
        content: `您的网站已成功部署，可通过以下链接公开访问：${deployUrl}`,
        okText: '复制链接',
        onOk: () => {
          copyToClipboard(deployUrl);
        }
      });
    } else {
      message.error(res.data?.message || '部署失败');
    }
  } catch (error) {
    console.error('部署失败:', error);
    message.error('部署失败，请稍后再试');
  } finally {
    deployLoading.value = false;
  }
};

// 复制到剪贴板
const copyToClipboard = (text: string) => {
  navigator.clipboard.writeText(text)
    .then(() => message.success('链接已复制到剪贴板'))
    .catch(() => message.error('复制失败，请手动复制'));
};

// 滚动到底部
const scrollToBottom = () => {
  nextTick(() => {
    if (messagesContainerRef.value) {
      messagesContainerRef.value.scrollTop = messagesContainerRef.value.scrollHeight;
    }
  });
};

// 返回首页
const goBack = () => { router.push('/'); };

// 部署按钮禁用
const deployBtnDisabled = computed(() => !codeGenerated.value || deployLoading.value);

// 代码生成模式显示
const codeGenMode = computed(() => {
  const t = (appInfo.value?.codeGenType || '').toLowerCase();
  if (!t) return { label: '未设置生成模式', color: 'default' } as const;
  if (t.includes('vue')) return { label: 'Vue3 工程模式', color: 'green' } as const;
  if (t.includes('multi')) return { label: 'HTML 多文件模式', color: 'purple' } as const;
  if (t.includes('html')) return { label: 'HTML 单文件模式', color: 'blue' } as const;
  return { label: t, color: 'default' } as const;
});

// 可折叠的对话：用户可控制每条消息展开/折叠
const collapsedIds = ref<Record<string, boolean>>({});
const toggleMsg = (id: string) => {
  collapsedIds.value[id] = !collapsedIds.value[id];
};

// 简易拖拽分割条
const isDragging = ref(false);
const chatWidthPercent = ref(15); // 初始 15%，右侧更大
const startDrag = (e: MouseEvent) => {
  isDragging.value = true;
  e.preventDefault();
};
const onDrag = (e: MouseEvent) => {
  if (!isDragging.value) return;
  const container = document.querySelector('.chat-container') as HTMLElement;
  if (!container) return;
  const rect = container.getBoundingClientRect();
  const pct = ((e.clientX - rect.left) / rect.width) * 100;
  chatWidthPercent.value = Math.min(80, Math.max(15, pct));
};
const stopDrag = () => { isDragging.value = false; };

onMounted(() => {
  window.addEventListener('mousemove', onDrag);
  window.addEventListener('mouseup', stopDrag);
});
onBeforeUnmount(() => {
  window.removeEventListener('mousemove', onDrag);
  window.removeEventListener('mouseup', stopDrag);
});

// 初始化
onMounted(async () => {
  if (!appId.value) { message.error('无效的应用ID'); router.push('/'); return; }
  const ok = await fetchAppInfo(); if (!ok) return;
  await fetchChatHistory();
  loading.value = false;
});

onBeforeUnmount(() => {
  if (eventSource.value) { eventSource.value.close(); eventSource.value = null; }
  // 不再在此处关闭构建进度 SSE，交由全局 store 与后端管理
});

// 键盘事件
const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } };

// 预览控制
const showPreview = ref(false);
const togglePreview = () => { showPreview.value = !showPreview.value; };
const previewFullscreen = ref(false);
const togglePreviewFullscreen = () => { previewFullscreen.value = !previewFullscreen.value; };

// 切换编辑模式：仅控制状态与提示，启用由 PreviewPane 接管
const toggleEditMode = async () => {
  if (!codeGenerated.value) { message.warning('请先生成网站后再进入编辑模式'); return; }
  if (!showPreview.value) { showPreview.value = true; await nextTick(); }
  isEditMode.value = !isEditMode.value;
  if (isEditMode.value) message.success('已进入编辑模式：在预览中点击元素可选中'); else message.info('已退出编辑模式');
};

function handleSelect(info: SelectedElementInfo) {
  const exists = selectedElements.value.some((it) => it.cssSelector === info.cssSelector);
  if (!exists) selectedElements.value.push(info);
}
function handleUnselect(selector: string) {
  selectedElements.value = selectedElements.value.filter((it) => it.cssSelector !== selector);
}

// 基于构建进度的提示状态
const buildState = computed(() => sseStore.buildStateByAppId[appId.value] || { status: 'idle', logs: [] as string[] });
const isBuilding = computed(() => {
  const isVue = (appInfo.value?.codeGenType || '').toLowerCase().includes('vue');
  const st = buildState.value.status as any;
  const hasLogs = (buildState.value.logs || []).length > 0;
  return isVue && (st === 'start' || (st === 'running' && hasLogs));
});
</script>

<template>
  <div class="chat-view">
    <Spin :spinning="loading" tip="加载中..." size="large">
      <div class="chat-header">
        <div class="left-section">
          <Button type="text" @click="goBack" class="back-button">
            <LeftOutlined />
          </Button>
          <h2 class="app-name">{{ appInfo?.appName || '应用对话' }}</h2>
          <Tag class="mode-tag" :color="codeGenMode.color">{{ codeGenMode.label }}</Tag>
        </div>
        <div class="right-section">
          <Tooltip :title="showPreview ? '隐藏预览' : '显示预览'">
            <Button type="default" @click="togglePreview" class="preview-toggle-button">
              <template #icon>
                <EyeOutlined v-if="!showPreview" />
                <EyeInvisibleOutlined v-else />
              </template>
              {{ showPreview ? '隐藏预览' : '显示预览' }}
            </Button>
          </Tooltip>
          <Tooltip title="下载应用代码">
            <Button type="default" :loading="downloadLoading" class="download-button" @click="handleDownload">
              <template #icon>
                <DownloadOutlined />
              </template>
              下载代码
            </Button>
          </Tooltip>
          <Tooltip :title="deployed ? '已部署' : '部署网站'">
            <Button type="primary" :disabled="deployBtnDisabled" :loading="deployLoading" class="deploy-button" @click="handleDeploy">
              <template #icon>
                <CheckCircleOutlined v-if="deployed" />
                <RocketOutlined v-else />
              </template>
              {{ deployed ? '已部署' : '部署' }}
            </Button>
          </Tooltip>
        </div>
      </div>

      <div class="chat-container" :class="{ dragging: isDragging }">
        <div class="chat-panel" :style="showPreview ? { width: chatWidthPercent + '%' } : {}" :class="{ 'full-width': !showPreview }">
          <div class="messages-container" ref="messagesContainerRef">
            <div v-if="sortedChatHistory.length === 0 && !isProcessingMessage" class="empty-chat">
              <div class="empty-chat-content">
                <p>开始与AI对话，生成您的网站应用</p>
              </div>
            </div>

            <template v-else>
              <div v-for="(msg, index) in sortedChatHistory" :key="msg.id || index" :class="['message-wrapper', msg.messageType === 'user' ? 'user-message' : 'ai-message']">
                <div class="message-avatar">
                  <Avatar :src="msg.messageType === 'user' ? userStore.loginUser.userAvatar : '/favicon.ico'" :size="36" />
                </div>
                <div class="message-content">
                  <div class="message-bubble" :class="msg.messageType !== 'user' ? 'markdown-message' : ''">
                    <div class="message-tools" v-if="msg.message">
                      <a @click="toggleMsg(msg.id)">{{ collapsedIds[msg.id] ? '展开' : '折叠' }}</a>
                    </div>
                    <div v-if="msg.messageType === 'user'">
                      <div v-show="!collapsedIds[msg.id]" class="message-text">{{ msg.message }}</div>
                    </div>
                    <div v-else class="message-text">
                      <div v-show="!collapsedIds[msg.id]" class="markdown-content">
                        <MarkdownViewer :content="msg.message || ''" />
                      </div>
                    </div>
                  </div>
                  <div class="message-time">{{ new Date(msg.createTime || '').toLocaleTimeString() }}</div>
                </div>
              </div>

              <!-- 当前正在生成的AI消息 -->
              <div v-if="isProcessingMessage" class="message-wrapper ai-message">
                <div class="message-avatar"><Avatar src="/favicon.ico" :size="36" /></div>
                <div class="message-content">
                  <div class="message-bubble markdown-message">
                    <div v-if="currentAiMessage" class="message-text">
                      <div class="markdown-content"><MarkdownViewer :content="currentAiMessage || ''" /></div>
                    </div>
                    <div v-else class="typing-indicator"><span></span><span></span><span></span></div>
                  </div>
                </div>
              </div>
            </template>
          </div>

          <div class="chat-input-container">
            <!-- 选中元素信息提示（多选 + 展示元素代码） -->
            <div v-if="selectedElements.length" class="selected-element-list">
              <Alert
                v-for="(el, i) in selectedElements"
                :key="el.cssSelector || i"
                type="info"
                show-icon
                closable
                @close="() => { previewPaneRef?.unselect?.(el.cssSelector || ''); selectedElements.splice(i,1); }"
                :message="`已选中元素：<${el.tagName}>`"
              >
                <template #description>
                  <div class="sel-desc">
                    <div class="sel-meta">
                      <span v-if="el.id">id: {{ el.id }}</span>
                      <span v-if="el.classList?.length">class: {{ el.classList.join(' ') }}</span>
                      <span v-if="el.text">文本: {{ el.text }}</span>
                    </div>
                    <div v-if="el.outerHTML" class="sel-code">
                      <pre><code>{{ el.outerHTML }}</code></pre>
                    </div>
                  </div>
                </template>
              </Alert>
            </div>

            <div class="input-wrapper">
              <Input.TextArea v-model:value="userMessage" placeholder="输入您的消息..." :rows="3" :disabled="isProcessingMessage" @keydown="handleKeyDown" class="chat-input" />
            </div>
            <div class="button-wrapper">
              <Button
                class="edit-toggle-button"
                :type="isEditMode ? 'primary' : 'default'"
                :ghost="isEditMode"
                :disabled="isProcessingMessage || !showPreview || !codeGenerated"
                @click="toggleEditMode"
              >
                <template #icon><CodeOutlined /></template>
                {{ isEditMode ? '退出编辑' : '编辑模式' }}
              </Button>

              <Button type="primary" :loading="chatLoading" :disabled="isProcessingMessage || !userMessage.trim()" @click="sendMessage" class="send-button">
                <template #icon><SendOutlined /></template>
                发送
              </Button>
            </div>
          </div>
        </div>

        <!-- 中间可拖拽分割条（仅在显示预览时展示） -->
        <div v-if="showPreview" class="splitter" @mousedown="startDrag"></div>

        <div v-if="showPreview" class="preview-panel" :class="{ 'preview-fullscreen': previewFullscreen }">
          <Card :bordered="false" class="preview-card">
            <template #title>
              <div class="preview-header">
                <span>网站预览</span>
                <div class="preview-actions">
                  <Tooltip title="刷新预览"><Button type="text" @click="refreshPreview"><template #icon><ReloadOutlined /></template></Button></Tooltip>
                  <Tooltip :title="previewFullscreen ? '退出全屏' : '全屏预览'"><Button type="text" @click="togglePreviewFullscreen" class="fullscreen-button"><template #icon><FullscreenExitOutlined v-if="previewFullscreen" /><FullscreenOutlined v-else /></template></Button></Tooltip>
                </div>
              </div>
            </template>
            <div class="preview-content">
              <div v-if="isBuilding" class="build-alert">
                <Alert
                  type="info"
                  show-icon
                  message="正在构建 Vue 项目，请不要离开此页面"
                  description="构建完成后请点击上方的刷新按钮更新网站预览。"
                />
              </div>
              <PreviewPane
                ref="previewPaneRef"
                :preview-url="previewUrl"
                :edit-mode="isEditMode"
                :code-generated="codeGenerated"
                :min-height="'80vh'"
                @select="handleSelect"
                @unselect="handleUnselect"
              />
            </div>
          </Card>
        </div>
      </div>

      <!-- 页面内全屏覆盖层，不使用浏览器 Fullscreen API -->
      <teleport to="body">
        <div v-if="previewFullscreen" class="preview-overlay">
          <div class="overlay-bar">
            <span>网站预览</span>
            <div class="overlay-actions">
              <Button type="text" @click="refreshPreview"><template #icon><ReloadOutlined /></template></Button>
              <Button type="text" @click="togglePreviewFullscreen"><template #icon><FullscreenExitOutlined /></template></Button>
            </div>
          </div>
          <div class="overlay-content">
            <div v-if="isBuilding" class="build-alert">
              <Alert
                type="info"
                show-icon
                message="正在构建 Vue 项目，请不要离开此页面"
                description="构建完成后请点击上方的刷新按钮更新网站预览。"
              />
            </div>
            <PreviewPane
              :preview-url="previewUrl"
              :edit-mode="isEditMode"
              :code-generated="codeGenerated"
              :min-height="'100vh'"
              @select="handleSelect"
              @unselect="handleUnselect"
            />
          </div>
        </div>
      </teleport>
    </Spin>
  </div>
</template>

<style scoped>
.chat-view {
  display: flex;
  flex-direction: column;
  width: 100%;
  position: relative;
}

.chat-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 0;
  border-bottom: 1px solid #f0f0f0;
  margin-bottom: 16px;
}

.left-section {
  display: flex;
  align-items: center;
}

.right-section {
  display: flex;
  align-items: center;
  gap: 8px;
}

.back-button {
  margin-right: 12px;
}

.app-name {
  margin: 0;
  font-size: 18px;
  font-weight: 500;
}

.preview-toggle-button {
  margin-right: 8px;
}

.chat-container {
  display: flex;
  flex: 1 1 auto;
  height: calc(100vh - 140px); /* 固定可用高度，避免整页滚动 */
  overflow: hidden; /* 外层隐藏溢出，仅中间消息区滚动 */
  gap: 12px;
  align-items: stretch;
}

.chat-panel {
  display: flex;
  flex-direction: column;
  width: 25%;
  height: 100%;
  border-radius: 8px;
  transition: width 0.3s ease;
  min-height: 0; /* 允许子元素设置滚动 */
}

.chat-panel.full-width {
  width: 100%;
}

.messages-container {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto; /* 仅消息区域滚动 */
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  background-color: #f5f5f5;
  border-radius: 8px;
}

.empty-chat {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: rgba(0, 0, 0, 0.45);
}

.message-wrapper {
  display: flex;
  margin-bottom: 16px;
}

.user-message {
  flex-direction: row-reverse;
}

.message-avatar {
  margin: 0 8px;
  flex-shrink: 0;
}

.message-content {
  max-width: 70%;
  display: flex;
  flex-direction: column;
}

.message-bubble {
  padding: 12px 16px;
  border-radius: 8px;
  position: relative;
}

.user-message .message-bubble {
  background-color: #1668dc;
  color: white;
  border-top-right-radius: 0;
}

.ai-message .message-bubble {
  background-color: white;
  color: rgba(0, 0, 0, 0.85);
  border-top-left-radius: 0;
}

.message-text {
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}

/* Markdown 渲染时不要强制 pre-wrap，否则会影响标题、列表、代码块的布局 */
.markdown-message .message-text {
  white-space: normal;
}

/* Bytemd 里的代码块滚动优化 */
.message-time {
  font-size: 12px;
  color: rgba(0, 0, 0, 0.45);
  margin-top: 4px;
  text-align: right;
}

.user-message .message-time {
  text-align: right;
}

.ai-message .message-time {
  text-align: left;
}

.typing-indicator {
  display: flex;
  gap: 4px;
  align-items: center;
}

.typing-indicator span {
  display: inline-block;
  width: 8px;
  height: 8px;
  background-color: rgba(0, 0, 0, 0.45);
  border-radius: 50%;
  animation: typing 1.4s infinite both;
}

.typing-indicator span:nth-child(2) {
  animation-delay: 0.2s;
}

.typing-indicator span:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes typing {
  0% {
    opacity: 0.2;
    transform: scale(0.8);
  }
  20% {
    opacity: 1;
    transform: scale(1);
  }
  100% {
    opacity: 0.2;
    transform: scale(0.8);
  }
}

.chat-input-container {
  padding: 16px;
  background: white;
  border-radius: 8px;
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.input-wrapper {
  width: 100%;
}

.chat-input {
  width: 100%;
  resize: none;
  border-radius: 8px;
}

.button-wrapper {
  display: flex;
  justify-content: flex-end;
}

.send-button {
  min-width: 100px;
}

.preview-panel {
  width: 75%;
  height: 100%;
  position: relative;
}

.preview-card {
  height: 100%;
  display: flex;
  flex-direction: column;
}

/* 减小卡片头部高度，增大可用预览高度 */
.preview-card :deep(.ant-card-head) {
  min-height: 44px;
  padding: 0 12px;
}

.preview-card :deep(.ant-card-head-title) {
  padding: 10px 0;
}

.preview-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.preview-actions {
  display: flex;
  align-items: center;
}

.preview-card :deep(.ant-card-body) {
  flex: 1;
  padding: 0;
  min-height: 72vh; /* 提高预览区域高度 */
  overflow: hidden;
}

.preview-content {
  height: 100%;
  min-height: 72vh;
  overflow: hidden;
  background-color: white;
  position: relative;
}

.preview-iframe {
  width: 100%;
  height: 100%;
  border: none;
  background-color: white;
}

.preview-fullscreen {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: 1000;
  background-color: white;
  padding: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
}

.preview-fullscreen .preview-card {
  flex: 1;
  height: 100%;
  box-shadow: none;
  border-radius: 0;
  margin: 0;
}

.preview-fullscreen .preview-card :deep(.ant-card-head) {
  min-height: 40px;
  padding: 0 16px;
  border-bottom: 1px solid #f0f0f0;
}

.preview-fullscreen .preview-card :deep(.ant-card-body) {
  padding: 0;
  height: calc(100% - 40px);
}

.preview-fullscreen .preview-content {
  height: 100%;
}

.preview-fullscreen .preview-iframe {
  height: 100%;
  border-radius: 0;
}

.fullscreen-button {
  cursor: pointer;
  margin-left: 8px;
}

.preview-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: rgba(0, 0, 0, 0.45);
  padding: 24px;
  text-align: center;
}

.code-message {
  background-color: #f8f9fa !important;
  padding: 8px !important;
  font-family: 'Consolas', 'Monaco', 'Courier New', Courier, monospace;
}

.markdown-message {
  background-color: white !important;
  padding: 12px 16px !important;
}

.code-block {
  margin: 0;
  white-space: pre-wrap;
  overflow-x: auto;
  max-height: 400px;
  border-radius: 4px;
}

.code-icon {
  margin-right: 8px;
  color: #1668dc;
}

.markdown-content {
  width: 100%;
}

.markdown-content :deep(h1),
.markdown-content :deep(h2),
.markdown-content :deep(h3),
.markdown-content :deep(h4),
.markdown-content :deep(h5),
.markdown-content :deep(h6) {
  margin-top: 0.5em;
  margin-bottom: 0.5em;
}

.markdown-content :deep(p) {
  margin-bottom: 1em;
}

.markdown-content :deep(ul),
.markdown-content :deep(ol) {
  padding-left: 20px;
  margin-bottom: 1em;
}

.markdown-content :deep(pre) {
  background-color: #f8f9fa;
  border-radius: 4px;
  padding: 12px;
  overflow-x: auto;
  margin-bottom: 1em;
}

.markdown-content :deep(code) {
  font-family: 'Consolas', 'Monaco', 'Courier New', Courier, monospace;
  background-color: #f8f9fa;
  padding: 2px 4px;
  border-radius: 3px;
}

.markdown-content :deep(blockquote) {
  border-left: 4px solid #ddd;
  padding-left: 16px;
  color: #666;
  margin-left: 0;
  margin-right: 0;
}

.markdown-content :deep(img) {
  max-width: 100%;
  height: auto;
}

.markdown-content :deep(table) {
  border-collapse: collapse;
  width: 100%;
  margin-bottom: 1em;
}

.markdown-content :deep(th),
.markdown-content :deep(td) {
  border: 1px solid #ddd;
  padding: 8px;
  text-align: left;
}

.markdown-content :deep(th) {
  background-color: #f8f9fa;
  font-weight: bold;
}

/***** 编辑模式 UI ******/
.selected-element-list :deep(.ant-alert) {
  margin-bottom: 8px;
}

/* 选中元素 Alert 中的布局与代码块样式 */
.sel-desc { display: flex; flex-direction: column; gap: 6px; }
.sel-meta { display: flex; flex-wrap: wrap; gap: 12px; font-size: 12px; color: rgba(0,0,0,0.65); }
.sel-code pre { margin: 0; max-height: 180px; overflow: auto; background: #f8f9fa; border-radius: 4px; padding: 8px; font-family: Menlo, Monaco, Consolas, monospace; font-size: 12px; }

/* 分割条可拖拽 */
.splitter {
  position: absolute;
  top: 0;
  left: -6px;
  width: 6px;
  height: 100%;
  cursor: col-resize;
  z-index: 10;
}

/* 分割条：位于中间，可左右拖动 */
.splitter {
  flex: 0 0 10px; /* 稍宽一点更易拖拽 */
  height: auto;
  cursor: col-resize;
  background-color: rgba(0, 0, 0, 0.15);
  border-radius: 6px;
  transition: background-color 0.2s ease, box-shadow 0.2s ease;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.2);
  z-index: 20;
}
.splitter:hover { background-color: rgba(0,0,0,0.28); }

/* 拖拽时全局显示为 col-resize 并禁用选择 */
.chat-container.dragging { cursor: col-resize; }
.chat-container.dragging * { cursor: col-resize !important; user-select: none !important; }

/* 右侧预览更大且更高 */
.preview-panel {
  flex: 1 1 auto;
  min-height: 72vh;
  position: relative;
}

/* 移除之前在预览内部的分割条样式（如存在） */
.divider { display: none; }

/* 页面内全屏覆盖层（不使用浏览器全屏） */
.preview-overlay {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  background: #fff;
  z-index: 2000;
  display: flex;
  flex-direction: column;
}
.overlay-bar {
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 12px;
  border-bottom: 1px solid #f0f0f0;
}
.overlay-actions { display: flex; align-items: center; }
.overlay-content { flex: 1; min-height: 0; }

.build-alert { padding: 12px; }
.build-alert :deep(.ant-alert) { margin-bottom: 8px; }

@media (max-width: 768px) {
  .chat-container {
    flex-direction: column;
  }

  .chat-panel, .preview-panel {
    width: 100%;
  }

  .chat-panel {
    height: 50%;
  }

  .preview-panel {
    height: 50%;
    margin-top: 16px;
  }

  .splitter { display: none; }
}
</style>
